package com.mycode.test;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.*;

import static com.mycode.util.ByteBufferUtil.debugAll;

/**
 * NIO 理解阻塞模式
 */
@Slf4j
public class Server {

    public static void main(String[] args) throws IOException {

        // 创建 Selector ，管理多个 channel
        Selector selector = Selector.open();

        ByteBuffer buffer = ByteBuffer.allocate(16);
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.bind(new InetSocketAddress(8080));

        // 2.建立 selector 和 channel 的联系（注册）
        // SelectionKey 就是将来事件发生后，通过它可以知道事件和哪个 channel 的事件
        /**
         * 事件类型
         * 1. accept：会在有连接请求时触发
         * 2. connect：是客户端，连接建立后触发
         * 3. read：有数据可读事件
         * 4. write：可写事件
         */
        SelectionKey selectionKey = serverSocketChannel.register(selector, 0, null);
        // SelectionKey 只关注 accept 事件
        selectionKey.interestOps(SelectionKey.OP_ACCEPT);
        log.debug("register key:{}", selectionKey);
        // 3. 连接集合
        List<SocketChannel> channelList = new ArrayList<>();
        while (true) {
            // select() 没有事件发生，线程阻塞，有事件，线程才恢复运行
            selector.select();
            // 处理事件 selectedKeys() 包含了所有发生的事件
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            log.debug("selectedKeys size : {}", selector.selectedKeys().size());
            if (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                log.debug("key: {}", key);
                // 5.区分事件类型
                // 如果是连接事件
                if (key.isAcceptable()) {
                    ServerSocketChannel serverSocketChannelTemp = (ServerSocketChannel) key.channel();
                    SocketChannel socketChannel = serverSocketChannelTemp.accept();
                    socketChannel.configureBlocking(false);
                    // sckey 负责
                    ByteBuffer byteBuffer = ByteBuffer.allocate(16);
                    // 添加附件: 将一个 bytebuffer 作为附件关联到 selectionKey 上
                    SelectionKey sckey = socketChannel.register(selector, 0, byteBuffer);
                    sckey.interestOps(SelectionKey.OP_READ);
                    log.debug("socketChannel:{}", socketChannel);
                    // 取消处理
                    //key.cancel();
                }
                // 如果是可读事件
                else if (key.isReadable()) {
                    try {
                        SocketChannel channel = (SocketChannel) key.channel(); // 拿到触发事件的 channel
                        // 获取到附件
                        ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
                        //ByteBuffer byteBuffer = ByteBuffer.allocate(4);
                        // 如果是正常断开 read 的方法的返回值是 -1
                        int read = channel.read(byteBuffer);
                        if (read == -1) {
                            key.cancel();
                        } else {
                            //byteBuffer.flip();
                            //debugAll(byteBuffer);
                            log.debug("服务器接收到数据:{}", Charset.defaultCharset().decode(byteBuffer).toString());
                            // 区分消息的边界
                            split(byteBuffer);
                            // bytebuffer 扩容
                            if (byteBuffer.position() == byteBuffer.limit()) {
                                ByteBuffer newBuffer = ByteBuffer.allocate(byteBuffer.capacity() * 2);
                                byteBuffer.flip();
                                newBuffer.put(byteBuffer);
                                // 替换原来的 bytebuffer
                                key.attach(newBuffer);
                            }

                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                        // 反注册，因为客户端断开了，因此需要将 key 取消（从 selector 的 keys 的集合中真正删除 key）
                        key.cancel();
                    }
                }
                // 处理 key 时，要从 selectedKeys 集合中删除
                iterator.remove();
            }

        }
    }

    private static void split(ByteBuffer source) {
        debugAll(source);
        // 切换到读模式
        source.flip();
        int oldLimit = source.limit();
        for (int i = 0; i < oldLimit; i++) {
            if (source.get(i) == '\n') {
                System.out.println(i);
                ByteBuffer target = ByteBuffer.allocate(i + 1 - source.position());
                // 0 ~ limit
                source.limit(i + 1);
                target.put(source); // 从source 读，向 target 写
                debugAll(target);
                source.limit(oldLimit);
            }
        }
        // 切换到写模式
        source.compact();
        debugAll(source);
    }

    /**
     * 非阻塞服务，如果长时间没有客户端连接，线程一直在循环等待
     * @throws IOException
     */
    private static void NonBlockServer() throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(16);

        // 1. 创建服务器
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        // 2. 绑定监听端口
        serverSocketChannel.bind(new InetSocketAddress(8080));
        // 设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        // 3. 连接集合
        List<SocketChannel> channelList = new ArrayList<>();
        while (true) {
            // 4.接受客户端的连接 SocketChannel 用来与客户端通信
            log.debug("connecting......");
            SocketChannel socketChannel = serverSocketChannel.accept(); // 非阻塞，线程还会继续运行，如果没有连接建立，但是 socketChannel 是 null
            if (Objects.nonNull(socketChannel)) {
                log.debug("connected...... {}", socketChannel);
                // SocketChannel 设置为非阻塞
                socketChannel.configureBlocking(false);
                channelList.add(socketChannel);
            }

            for (SocketChannel channel : channelList) {
                log.debug("before read... {}", channel);
                int read = channel.read(buffer);// 非阻塞方法，线程仍然会继续运行，如果没有读到数据，read 返回 0
                if (read > 0) {
                    // 切换读模式
                    buffer.flip();
                    debugAll(buffer);
                    // 切换 写模式
                    buffer.clear();
                    log.debug("after read... {}", channel);
                }
            }
        }
    }

    /**
     * 阻塞服务器
     * @throws IOException
     */
    private static void blockServer() throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(16);

        // 1. 创建服务器
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

        // 2. 绑定监听端口
        serverSocketChannel.bind(new InetSocketAddress(8080));
        // 设置为非阻塞
        serverSocketChannel.configureBlocking(false);
        // 3. 连接集合
        List<SocketChannel> channelList = new ArrayList<>();
        while (true) {
            // 4.接受客户端的连接 SocketChannel 用来与客户端通信
            log.debug("connecting......");
            SocketChannel socketChannel = serverSocketChannel.accept(); // 非阻塞，线程还会继续运行，如果没有连接建立，但是 socketChannel 是 null
            log.debug("connected...... {}", socketChannel);
            channelList.add(socketChannel);
            for (SocketChannel channel : channelList) {
                // 5.接受客户端发送的数据
                log.debug("before read... {}", channel);
                channel.read(buffer); // 阻塞方法，线程停止运行
                // 切换读模式
                buffer.flip();
                debugAll(buffer);
                // 切换 写模式
                buffer.clear();
                log.debug("after read... {}", channel);
            }
        }
    }

}
